package com.example.demo.security;

import com.example.demo.exception.ErrorResponse;
import com.example.demo.exception.JwtAuthenticationException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.util.stream.Collectors;

@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private static final String AUTHORIZATION_HEADER = "Authorization";
    private static final String BEARER_PREFIX = "Bearer ";
    private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    // 不需要JWT认证的路径
    private static final List<String> EXCLUDED_PATHS = Arrays.asList(
            "/api/auth/**",
            "/swagger-ui/**",
            "/v3/api-docs/**",
            "/webjars/**",
            "/swagger-resources/**",
            "/favicon.ico",
            "/actuator/health",
            "/error"
    );

    private final JwtTokenProvider jwtTokenProvider;
    private final ObjectMapper objectMapper;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        try {
            String jwt = resolveToken(request);
            if (StringUtils.hasText(jwt)) {
                Claims claims = jwtTokenProvider.validateAndGetClaims(jwt);

                // 1. 安全提取用户信息
                String username = claims.getSubject();
                Long userId = extractUserId(claims);
                String userType = extractUserType(claims);
                List<String> roles = extractRoles(claims);

                // 2. 构建权限列表（确保ROLE_前缀）
                Collection<GrantedAuthority> authorities = roles.stream()
                        .map(role -> role.startsWith("ROLE_") ? role : "ROLE_" + role)
                        .map(SimpleGrantedAuthority::new)
                        .collect(Collectors.toList());

                // 3. 创建自定义Authentication对象
                CustomUserDetails userDetails = new CustomUserDetails(
                        userId,
                        username,
                        "", // 密码不需要
                        userType,
                        authorities
                );

                Authentication authentication = new UsernamePasswordAuthenticationToken(
                        userDetails,
                        null,
                        authorities
                );

                SecurityContextHolder.getContext().setAuthentication(authentication);
                log.debug("Authenticated user: {}, userId: {}, userType: {}, roles: {}",
                        username, userId, userType, roles);
            }
            filterChain.doFilter(request, response);
        } catch (Exception ex) {
            handleJwtException(response, ex);
        }
    }

    private Long extractUserId(Claims claims) {
        try {
            Object userIdClaim = claims.get("user_id");
            if (userIdClaim == null) {
                throw new JwtAuthenticationException("Missing user ID in token", "MISSING_USER_ID");
            }

            // 处理所有可能的数字类型
            if (userIdClaim instanceof Number) {
                return ((Number) userIdClaim).longValue();
            }
            return Long.parseLong(userIdClaim.toString());
        } catch (NumberFormatException e) {
            throw new JwtAuthenticationException("Invalid user ID format in token", "INVALID_USER_ID_FORMAT");
        } catch (Exception e) {
            throw new JwtAuthenticationException("Invalid user ID in token", "INVALID_USER_ID");
        }
    }

    private String extractUserType(Claims claims) {
        try {
            Object userTypeClaim = claims.get("user_type");
            if (userTypeClaim == null) {
                throw new JwtAuthenticationException("Missing user type in token", "MISSING_USER_TYPE");
            }
            return userTypeClaim.toString();
        } catch (Exception e) {
            throw new JwtAuthenticationException("Invalid user type in token", "INVALID_USER_TYPE");
        }
    }

    @SuppressWarnings("unchecked")
    private List<String> extractRoles(Claims claims) {
        try {
            Object rolesClaim = claims.get("roles");
            if (rolesClaim instanceof List) {
                return (List<String>) rolesClaim;
            }
            return Collections.singletonList(rolesClaim.toString());
        } catch (Exception e) {
            throw new JwtAuthenticationException("Invalid roles in token", "INVALID_ROLES");
        }
    }

    private void handleJwtException(HttpServletResponse response, Exception ex) throws IOException {
        if (ex instanceof ExpiredJwtException) {
            handleJwtError(response, "Token expired", "JWT_EXPIRED", HttpServletResponse.SC_UNAUTHORIZED);
        } else if (ex instanceof UnsupportedJwtException) {
            handleJwtError(response, "Unsupported JWT token", "JWT_UNSUPPORTED", HttpServletResponse.SC_BAD_REQUEST);
        } else if (ex instanceof MalformedJwtException) {
            handleJwtError(response, "Invalid JWT token", "JWT_INVALID", HttpServletResponse.SC_BAD_REQUEST);
        } else if (ex instanceof JwtException) {
            handleJwtError(response, "JWT verification failed", "JWT_VERIFICATION_FAILED", HttpServletResponse.SC_UNAUTHORIZED);
        } else if (ex instanceof JwtAuthenticationException) {
            handleJwtError(response, ex.getMessage(), ((JwtAuthenticationException) ex).getErrorCode(), HttpServletResponse.SC_UNAUTHORIZED);
        } else {
            handleJwtError(response, "Authentication failed", "AUTH_FAILURE", HttpServletResponse.SC_UNAUTHORIZED);
        }
    }

    private String resolveToken(HttpServletRequest request) {
        String bearerToken = request.getHeader(AUTHORIZATION_HEADER);
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(BEARER_PREFIX)) {
            return bearerToken.substring(BEARER_PREFIX.length());
        }
        return null;
    }

    private void handleJwtError(HttpServletResponse response,
                                String message,
                                String errorCode,
                                int status) throws IOException {
        log.warn("JWT authentication failed: {} (code: {})", message, errorCode);

        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setStatus(status);

        ErrorResponse errorResponse = new ErrorResponse(status, message, errorCode);
        objectMapper.writeValue(response.getWriter(), errorResponse);
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        String path = request.getServletPath();
        return EXCLUDED_PATHS.stream()
                .anyMatch(pattern -> PATH_MATCHER.match(pattern, path));
    }
}